#include "stdafx.h"
#include "stream/base64decode.hpp"
#include "stream/base64encode.hpp"
#include "event/mutex.hpp"
using namespace JHCPP::event;
#include "stdlib/exception.hpp"
using namespace JHCPP::stdlib::jhException;

namespace JHCPP
{
	namespace stream
	{
		unsigned char CBase64DecodeBuf::IN_ENCODING[256];
		bool CBase64DecodeBuf::IN_ENCODING_INIT = false;
		static CMutex mutex;

		CBase64DecodeBuf::CBase64DecodeBuf(std::istream& istr)
		: m_groupLength(0),
		m_groupIndex(0),
		m_buf(*istr.rdbuf())
		{
			CMutex::ScopedLock lock(mutex);
			if (!IN_ENCODING_INIT)
			{
				for (unsigned i = 0; i < sizeof(IN_ENCODING); i++)
				{
					IN_ENCODING[i] = 0xFF;
				}
				for (unsigned i = 0; i < sizeof(CBase64EncodeBuf::OUT_ENCODING); i++)
				{
					IN_ENCODING[CBase64EncodeBuf::OUT_ENCODING[i]] = i;
				}
				IN_ENCODING[static_cast<unsigned char>('=')] = '\0';
				IN_ENCODING_INIT = true;
			}
		}


		CBase64DecodeBuf::~CBase64DecodeBuf()
		{
		}

		int CBase64DecodeBuf::readFromDevice()
		{
			if (m_groupIndex < m_groupLength) 
			{
				return m_group[m_groupIndex++];
			}
			else
			{
				unsigned char buffer[4];
				int c;
				if ((c = readOne()) == -1) return -1;
				buffer[0] = (unsigned char) c;
				if (IN_ENCODING[buffer[0]] == 0xFF) throw DataFormatException();
				if ((c = readOne()) == -1) throw DataFormatException();
				buffer[1] = (unsigned char) c;
				if (IN_ENCODING[buffer[1]] == 0xFF) throw DataFormatException();
				if ((c = readOne()) == -1) throw DataFormatException();
				buffer[2] = c;
				if (IN_ENCODING[buffer[2]] == 0xFF) throw DataFormatException();
				if ((c = readOne()) == -1) throw DataFormatException();
				buffer[3] = c;
				if (IN_ENCODING[buffer[3]] == 0xFF) throw DataFormatException();

				m_group[0] = (IN_ENCODING[buffer[0]] << 2) | (IN_ENCODING[buffer[1]] >> 4);
				m_group[1] = ((IN_ENCODING[buffer[1]] & 0x0F) << 4) | (IN_ENCODING[buffer[2]] >> 2);
				m_group[2] = (IN_ENCODING[buffer[2]] << 6) | IN_ENCODING[buffer[3]];

				if (buffer[2] == '=')
					m_groupLength = 1;
				else if (buffer[3] == '=') 
					m_groupLength = 2;
				else
					m_groupLength = 3;
				m_groupIndex = 1;
				return m_group[0];
			}
		}

		int CBase64DecodeBuf::readOne()
		{
			int ch = m_buf.sbumpc();
			while (ch == ' ' || ch == '\r' || ch == '\t' || ch == '\n')
				ch = m_buf.sbumpc();
			return ch;
		}


		CBase64DecodeIOS::CBase64DecodeIOS(std::istream& istr) : m_buf(istr)
		{

		}

		CBase64DecodeIOS::~CBase64DecodeIOS()
		{
		}

		CBase64DecodeBuf* CBase64DecodeIOS::rdbuf()
		{
			return &m_buf;
		}


		CBase64Decode::CBase64Decode(std::istream& istr): CBase64DecodeIOS(istr), std::istream(&m_buf)
		{
		}

		CBase64Decode::~CBase64Decode()
		{
		}

	}//end of namespace stream
}//end of namespace JHCPP
